package util;

import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.geom.AffineTransform;
import java.awt.geom.Point2D;
import java.awt.image.BufferedImage;
import java.util.ArrayList;

public class ImageUtil {
   
    public static short[][] bitmapToByteMatrix(BufferedImage src)
    {
    	int h = src.getHeight();
    	int w = src.getWidth();
    	short[][] slika = new short[h][w];
        for (int y = 0; y < h; y++)
        {
            for (int x = 0; x < w; x++)
            {
            	int rgb = src.getRGB(x, y);

                Color c = new Color(rgb);

                short r = (short) Math.abs(c.getRed());   
                short g = (short) Math.abs(c.getGreen());
                short b = (short) Math.abs(c.getBlue()) ;
                short prosek =  (short)((b + g + r) / 3);

            	slika[y][x] = prosek;
            }
        }
        return slika;
    }

    public static BufferedImage matrixToBitmap(short[][] slika)
    {
    	int h = slika.length;
        int w = slika[0].length;
    	BufferedImage bufferedImage = new BufferedImage(w, h, BufferedImage.TYPE_INT_RGB);
        for (int y = 0; y < h; y++)
        {
            for (int x = 0; x < w; x++)
            {
            	Color c = new Color(slika[y][x], slika[y][x], slika[y][x]);
            	bufferedImage.setRGB(x, y, c.getRGB());
            }
        }
        return bufferedImage;
    }
    
    public static  BufferedImage colorMatrixToBitmap(short[][][] slika)
    {
    	int h = slika.length;
        int w = slika[0].length;
    	BufferedImage bufferedImage = new BufferedImage(w, h, BufferedImage.TYPE_INT_RGB);
        for (int y = 0; y < h; y++)
        {
            for (int x = 0; x < w; x++)
            {
            	Color c = new Color(slika[y][x][0], slika[y][x][1], slika[y][x][2]);
            	bufferedImage.setRGB(x, y, c.getRGB());
            }
        }
        return bufferedImage;
    }

    public static short[][][] bitmapToColorMatrix(BufferedImage src)
    {
    	int h = src.getHeight();
    	int w = src.getWidth();
    	short[][][] slika = new short[h][w][3];
        for (int y = 0; y < h; y++)
        {
            for (int x = 0; x < w; x++)
            {
            	int rgb = src.getRGB(x, y);

                Color c = new Color(rgb);

                slika[y][x][0] = (short) Math.abs(c.getRed());   
                slika[y][x][1] = (short) Math.abs(c.getGreen());
                slika[y][x][2] = (short) Math.abs(c.getBlue()) ;

            }
        }
        return slika;
    }

    public static double mean(short[][] slika)
    {
       	int h = slika.length;
        int w = slika[0].length;
        double mean = 0;
        for (int y = 0; y < h; y++)
        {
            for (int x = 0; x < w; x++)
            {
                mean += slika[y][x];
            }
        }
        mean = mean / (w * h);
        return mean;
    }

    public static short[][] matrixToBinary(short[][] slika, double mean)
    {
       	int h = slika.length;
        int w = slika[0].length;
        short[][] retVal = new short[h][w];
        for (int y = 0; y < h; y++)
        {
            for (int x = 0; x < w; x++)
            {
                if (slika[y][x] < mean)
                    retVal[y][x] = 0;
                else
                    retVal[y][x] = 255;
            }
        }
        return retVal;
    }

   public static short[][] matrixToBinaryTiles(short[][] slika, int R, int C)
    {
	  	int h = slika.length;
        int w = slika[0].length;
        double dW = (double)w / C;
        double dH = (double)h / R;
        double[][] means = new double[R][C];
        short[][] retVal = new short[h][w];
        int D = 4;
        for (int r = 0; r < R; r++)
        {
            for (int c = 0; c < C; c++)
            {
                means[r][c] = 0;
                int minD = 0;
                int maxD = 0;
                int maxDif = 0;
                for (int y = 0; y < dH; y++)
                {
                    int A = 0;
                    int B = 0;
                    for (int x = 0; x < D; x++)
                    {
                        A += slika[(int)(r * dH) + y][(int)(c * dW) + x];
                    }
                    for (int x = D; x < 2 * D; x++)
                    {
                        B += slika[(int)(r * dH) + y][(int)(c * dW) + x];
                    }
                    for (int x = D; x < dW - D; x++)
                    {
                        int diff = Math.abs(A - B);
                        if (diff >= maxDif)
                        {
                            maxDif = diff;
                            minD = Math.min(A, B);
                            maxD = Math.max(A, B);
                        }
                        A -= slika[(int)(r * dH) + y][ (int)(c * dW) + x - D];
                        A += slika[(int)(r * dH) + y][(int)(c * dW) + x];
                        B -= slika[(int)(r * dH) + y][(int)(c * dW) + x];
                        B += slika[(int)(r * dH) + y][(int)(c * dW) + x + D - 1];
                    }
                }
                int TT = (maxD + minD) / (2 * D);
                int DD = (maxD - minD) / D;
                for (int y = 0; y < dH; y++)
                {
                    for (int x = 0; x < dW; x++)
                    {
                        if (DD > 20)
                        {
                            if (slika[(int)(r * dH) + y][(int)(c * dW) + x] < TT)//means[r, c])
                                retVal[(int)(r * dH) + y][ (int)(c * dW) + x] = 0;
                            else
                                retVal[(int)(r * dH) + y][(int)(c * dW) + x] = 255;
                        }
                        else
                        {
                            if (TT < 80)
                                retVal[(int)(r * dH) + y][(int)(c * dW) + x] = 0;
                            else
                                retVal[(int)(r * dH) + y][(int)(c * dW) + x] = 255;
                        }
                    }
                }
            }
        }
        return retVal;
    }

    public static short[][] erosion(short[][] slika)
    {
     	int h = slika.length;
        int w = slika[0].length;
        short[][] retVal = new short[h][w];
        int[] ii = { 0, 1, 1, 1, 0, -1, -1, -1, 0 };
        int[] jj = { 1, 1, 0, -1, -1, -1, 0, 1, 0 };
        int n = ii.length;
        for (int y = 0; y < h; y++)
        {
            for (int x = 0; x < w; x++)
            {
                Boolean b = true;
                for (int t = 0; t < n; t++)
                {
                    if (y + ii[t] < 0 || y + ii[t] >= h
                        || x + jj[t] < 0 || x + jj[t] >= w)
                        continue;
                    
                    if (slika[y + ii[t]][x + jj[t]] != 0) // NIJE CRNA TACKA 
                    {
                        b = false;
                        break;
                    }
                }
                if (b == true)
                    retVal[y][x] = 0;
                else
                    retVal[y][x] = 255;
            }
        }
        return retVal;
    }

    public static short[][] dilation(short[][] slika)
    {
     	int h = slika.length;
        int w = slika[0].length;
        short[][] retVal = new short[h][w];
        int[] ii = { 0, 1, 1, 1, 0, -1, -1, -1, 0 };
        int[] jj = { 1, 1, 0, -1, -1, -1, 0, 1, 0 };
        int n = ii.length;
        for (int y = 0; y < h; y++)
        {
            for (int x = 0; x < w; x++)
            {
                Boolean b = false;
                for (int t = 0; t < n; t++)
                {
                    if (y + ii[t] < 0 || y + ii[t] >= h
                       || x + jj[t] < 0 || x + jj[t] >= w)
                        continue;

                    if (slika[y + ii[t]][x + jj[t]] == 0) // BAR JEDNA CRNA TACKA 
                    {
                        b = true;
                        break;
                    }
                }
                if (b == true)
                    retVal[y][ x] = 0;
                else
                    retVal[y][x] = 255;
            }
        }
        return retVal;
    }
    

    public static ArrayList<RasterRegion> regionLabeling(short[][] slika)
    {
    	ArrayList<RasterRegion> regions = new ArrayList<RasterRegion>();
       	int h = slika.length;
        int w = slika[0].length;
        int[] ii = { 0, 1, 1, 1, 0, -1, -1, -1 };
        int[] jj = { 1, 1, 0, -1, -1, -1, 0, 1 };
        int n = ii.length;
        short regNum = 0;
        for (int y = 1; y < h - 1; y++)
        {
            for (int x = 1; x < w - 1; x++)
            {
                if (slika[y][x] == 0)
                {
                    regNum++;
                    short rr = (short)(regNum * 50);
                    if (rr == 0)
                        rr = 1;
                    slika[y][x] = (rr > 255) ? 255 : rr;
                    ArrayList<Point2D> front = new ArrayList<Point2D>();
                    Point2D pt = new Point2D.Double(x, y);
                    RasterRegion region = new RasterRegion();
                    region.regId = regNum;
                    region.points.add(pt);
                    regions.add(region);
                    front.add(pt);
                    while (front.size() > 0)
                    {
                        Point2D p = front.get(0);
                        front.remove(0);
                        for (int t = 0; t < n; t++)
                        {
                            Point2D point = new Point2D.Double(p.getX() + jj[t], p.getY() + ii[t]);
                            if (point.getX() > -1 && point.getX() < w && point.getY() > -1 && point.getY() < h)
                            {
                                short pp = slika[(int)point.getY()][(int)point.getX()];
                                if (pp == 0)
                                {
                                    slika[(int)point.getY()][(int)point.getX()] = slika[y][x];
                                    region.points.add(point);
                                    front.add(point);
                                }
                            }
                        }
                    }
                }
            }
        }
        return regions;
    }

    public static short[][] iviceSobel(short[][] slika)
    {
     	int h = slika.length;
        int w = slika[0].length;
        short[][] nslika = new short[h][w];
        int[][] maskaA = {{-1, 0, 1}, 
                         {-2, 0, 2},
                         {-1, 0, 1}};
        int[][] maskaB = {{-1, -2, -1}, 
                         { 0,  0,  0},
                         { 1,  2,  1}};
        for (int y = 0; y < h; y++)
        {
            for (int x = 0; x < w; x++)
            {
                nslika[y][x] = (short)(0);
            }
        }
        for (int y = 1; y < h - 1; y++)
        {
            for (int x = 1; x < w - 1; x++)
            {
                int sumA = 0;
                int sumB = 0;
                for (int yy = -1; yy < 2; yy++)
                    for (int xx = -1; xx < 2; xx++)
                    {
                        sumA += maskaA[yy + 1][xx + 1] * (int)slika[y + yy][x + xx];
                        sumB += maskaB[yy + 1][xx + 1] * (int)slika[y + yy][x + xx];
                    }
                double s = sumA * sumA + sumB * sumB;
                if(Math.sqrt(s) > 255)
                	System.out.println("");
                nslika[y][x] = (short)(Math.min(Math.sqrt(s),255));
            }
        }
        return nslika;
    }

    public static short[][] invert(short[][] slika)
    {
    	int h = slika.length;
        int w = slika[0].length;
        short[][] nslika = new short[h][w];
        for (int y = 0; y < h ; y++)
        {
            for (int x = 0; x < w ; x++)
            {
                nslika[y][x] = (short)(255 - slika[y][x]);
            }
        }
        return nslika;
    }

    public static short[][] diff(short[][] slikaA, short[][] slikaB)
    {
      	int h = slikaA.length;
        int w = slikaA[0].length;
        short[][] nslika = new short[h][w];
        for (int y = 1; y < h - 1; y++)
        {
            for (int x = 1; x < w - 1; x++)
            {
                nslika[y][ x] = (short)Math.abs(slikaB[y][x] - slikaA[y][x]);
            }
        }
        return nslika;
    }
 
 

    public static short[][] blur(short[][] slika)
    {
      	int h = slika.length;
        int w = slika[0].length;
        short[][] retVal = new short[h][w];
        int[] ii = { 0, 1, 1, 1, 0, -1, -1, -1, 0 };
        int[] jj = { 1, 1, 0, -1, -1, -1, 0, 1, 0 };
        int sum = 0;
        for (int y = 0; y < h; y++)
        {
            for (int x = 0; x < w; x++)
            {
                sum = 0;
                int n = 0;
                for (int q = 0; q < ii.length - 1; q++){
                	  if (y + ii[q] < 0 || y + ii[q] >= h
                      || x + jj[q] < 0 || x + jj[q] >= w)
                       continue;
                    sum += slika[y + ii[q]][x + jj[q]];
                    n++;
                }
                retVal[y][x] = (short)(sum / n);
            }
        }
        return retVal;
    }

 
    public static short[][] resizeImage(short[][] src, Dimension size)
    {
    	BufferedImage imgToResize = matrixToBitmap(src);

        int sourceWidth = imgToResize.getWidth();
        int sourceHeight = imgToResize.getHeight();

        float nPercent = 0;
        float nPercentW = 0;
        float nPercentH = 0;
        nPercentW = ((float)size.width / (float)sourceWidth);
        nPercentH = ((float)size.height / (float)sourceHeight);

        if (nPercentH < nPercentW)
            nPercent = nPercentH;
        else
            nPercent = nPercentW;

        int destWidth = (int)(sourceWidth * nPercent);
        int destHeight = (int)(sourceHeight * nPercent);
    	
        int offX = (size.width - destWidth) / 2;
        int offY = (size.height - destHeight) / 2;
        
        BufferedImage b = new BufferedImage(size.width, size.height, BufferedImage.TYPE_INT_RGB);
        Graphics2D g = b.createGraphics();   
        g.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BICUBIC);  
        g.drawImage(imgToResize, offX, offY, destWidth, destHeight, null);
        g.dispose();
        return bitmapToByteMatrix(b);
    }
}
